//asio 实现全双工异步通信 - 解决应答式通信不灵活的问题

#include <boost/asio/io_context.hpp>
#include <utility>
#include <memory>
#include <string.h>
#include <mutex>
#include <string>
#include <queue>
#include <map>

#include <boost/asio.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>


using boost::asio::ip::tcp;

class MsgNode
{
    friend class Session;
public:
    MsgNode(char* data, size_t len)
    :m_cur_len(0)
    ,m_max_len(len)
    ,m_data(new char[len])
    {
        memcpy(m_data.get(), data, len);
    }
private:
    size_t m_cur_len;   //已经处理的数据长度
    size_t m_max_len;   //数据总长度
    std::unique_ptr<char[]> m_data;
};


class Server;

class Session: public std::enable_shared_from_this<Session>
{
public:
    Session(boost::asio::io_context& ioc, Server* server)
    :m_socket(ioc)
    ,m_server(server)
    ,m_read_buf(new char[m_max_len])
    {
        m_uuid = boost::uuids::to_string(boost::uuids::random_generator()());
    }

    void Start();
    void Send(char* msg, size_t len);
    tcp::socket& GetSocket() { return m_socket;}
    const std::string& GetUuid() const { return m_uuid; }
private:

    void _HadnleRead(const boost::system::error_code& ec, size_t len, std::shared_ptr<Session> self_ptr);
    void _HandleWrite(const boost::system::error_code& ec, std::shared_ptr<Session> self_ptr);
    
private:
    const uint32_t m_max_len = 1024;

    tcp::socket m_socket;   //客户端socket
    Server*     m_server;   //server
    std::string m_uuid;     //唯一标示
    std::unique_ptr<char[]> m_read_buf; //读缓冲区
    std::mutex  m_send_lock;
    std::queue<std::shared_ptr<MsgNode>> m_send_que; //发送队列
};

class Server
{
public:

    Server(boost::asio::io_context&ioc, uint16_t port);
    void DeleteSession(const std::string& uuid);
private:    

    //使用智能指针解决程序崩溃问题
    void _StartAccetp();
    void _HandleAccept(std::shared_ptr<Session> session, const boost::system::error_code& ec);
private:
    boost::asio::io_context& m_ioc; //io上下文 不可以拷贝，只能引用
    tcp::acceptor m_acceptor;       //监听客户端的连接

    std::map<std::string, std::shared_ptr<Session>> m_sessions; 
    std::mutex m_mutex;
};